/*
*	目的：这个代码实现了一个 媒体库 的功能，涉及到多个频道的处理，每个频道都有描述文件和多个音频数据文件（例如MP3文件）
*	功能：读取频道列表、读取每个频道的描述信息，以及根据请求返回某个频道的音频数据
*/

#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include "mlib.h"

#define CHNPATH		"/home/shianzhen/media/mdialib/*"
#define DESPATH		"/home/shianzhen/media/mdialib/*/*.txt"
#define DATAPATH	"/home/shianzhen/media/mdialib/*/*.mp3"

#define PATHSIZE	256

//定义存储信息的结构体 chn_st
struct chn_st {
	//用于存储该频道的描述信息和其他相关数据（如chnid 和 descr）
	struct mlib_st mylib1;
	//存储该频道下所有匹配的MP3文件路径信息。glob_t是glob函数的返回类型，它保存了所有匹配模式的路径信息
	glob_t res;
	//当前打开的音频文件的文件描述符
	int fd;
	//记录要读取的音频文件的索引。用于循环播放所有MP3文件（由于在main.c中规定了）
	int next;
};

//全局变量
//all_chn[MAXCHN]：存储所有频道的数组，每个元素是一个 chn_st 类型的指针，代表一个频道。
static struct chn_st *all_chn[MAXCHN];

//第一步：初始化频道信息
//包括读取频道路径、描述文件、以及频道下的MP3文件路径。
//返回的频道信息会通过 mylib 参数传递回调用者
//参数1：指向mlib_st数组的指针，用于存储所有频道的信息
//参数2：指向整数的指针，用于存储频道的数量
int mylib_get_chn_list(struct mlib_st **mylib, int *mylibnr)
{
	glob_t res_chn;   //定义存放 频道路径 的结构体
	glob_t res_des;   //定义存放 描述文件 的结构体 .txt
	int i;   
	int fd;   //文件描述符
	int cnt;  //
	char buf[BUFSIZE];    //
	char path[PATHSIZE];  //

	//所有频道的路径
	//使用 glob() 函数获取所有频道的路径。
	if(0 != glob(CHNPATH, 0, NULL, &res_chn)) {
		perror("glog(res_chn):");
		return -1;
	}
	//所有频道下的.txt文件的路径
	if(0 != glob(DESPATH, 0, NULL, &res_des)) {
		perror("glob(res_des):");
		return -1;
	}

	*mylibnr = res_chn.gl_pathc;//频道个数

	//为频道分配内存：每个频道的信息用一个 mlib_st结构体表示
	*mylib = (struct mlib_st *)malloc(sizeof(struct mlib_st) * (*mylibnr));
	if(NULL == *mylib) {
		fprintf(stderr, "malloc():\n");
		return -1;
	}

	for(i = 0; i < *mylibnr; i++) {
		//为每个频道初始化数据：
		all_chn[i] = malloc(sizeof(struct chn_st));
		if(NULL == all_chn[i]) {
			fprintf(stderr, "malloc() all_chn failed\n");
			return -1;
		}

		//将path 所指向的内存区域的前PATHSIZE字节全部设置为 "\0"
		memset(path, '\0', PATHSIZE);
		//将res_chn.gl_pathvp[i] 复制到 path字符数组中
		//注意：  strlen()并不会计算 "\0"，因此在复制过程中会省略 '\0'
		strncpy(path, res_chn.gl_pathv[i], strlen(res_chn.gl_pathv[i]));
		//将 "/*.mp3" 追加到 path 目标字符串中
		strcat(path, "/*.mp3");

		//判断使用glob()函数获取所有 .mp3文件路径是否成功
		if(0 != glob(path, 0, NULL, &(all_chn[i]->res))) {
			perror("glob() failed");
			return -1;
		}

		//读取描述文件：打开描述文件并读取文件内容（例如频道的名称、信息等）
		fd = open(res_des.gl_pathv[i], O_RDONLY);
		if(fd < 0) {
			perror("open .txt failed:");
			return -1;
		}
		//将buf 所指向的内存区域的前BUFSIZE字节全部设置为 "\0"
		memset(buf, '\0', BUFSIZE);
		//读取fd 内容存放到 buf 指向的缓冲区中
		cnt = read(fd, buf, BUFSIZE);
		if(0 == cnt) {
			break;
		}else if(cnt < 0) {
			perror("read .txt failed:");
			close(fd);
			return -1;
		}


		//将描述文件中的内容存储在 mylib_st 结构体中的 descr 字段，并为每个频道设置唯一的频道号 chnid
		((*mylib) + i)->descr = malloc(BUFSIZE);
		strcpy(((*mylib) + i)->descr, buf);
		//printf("%s", buf);
		//printf("%s", ((*mylib) + i)->descr);
		((*mylib) + i)->chnid = i + 1;

		//设置 next 为 -1 ：表示该频道的 MP3文件尚未被读取
		all_chn[i]->next = -1;
		all_chn[i]->mylib1 = (*mylib)[i]; 
	}

	//0表示成功，非0表示失败
	return 0;
}

//这个函数用于读取某个频道的音频数据。根据需要循环读取该频道下的MP3文件，并返回读取的数据
//参数1：要读取的频道的频道号
//参数2：用于存储读取的音频数据的缓冲区
//参数3：读取的最大字节数
int mylib_read_chn_data(int chnid, void *buf, int size)
{
	int cnt;

	//初始化文件读取：
	//如果该频道还没打开任何音频文件（next == -1），就将 next 设置为0，并打开该频道下的第一个MP3文件
	if(-1 == all_chn[chnid]->next) {
		all_chn[chnid]->next = 0;
		all_chn[chnid]->fd = open(all_chn[chnid]->res.gl_pathv[all_chn[chnid]->next], O_RDONLY);
		if(all_chn[chnid]->fd < 0) {
			perror("open() all_chn failed:");
			return -1;
		}
	}

	//读取数据：从当前打开的文件中读取音频数据，并将其存储到 buf 中
	cnt = read(all_chn[chnid]->fd, buf, size);
	
	//如果 read() 函数返回值为-1 表示读取失败，函数返回-1
	if(cnt < 0) {
		perror("data read():");
		return -1;
	}
	//如果读取的字节数为 0 ，表示当前文件已经读取完毕，接着读取下一个 MP3 文件
	//读取完当前文件后， next索引递增，循环访问下一个 MP3 文件，直到所有文件都读取完。
	if(cnt == 0) {
		all_chn[chnid]->next++;
		all_chn[chnid]->next = all_chn[chnid]->next % all_chn[chnid]->res.gl_pathc;
		all_chn[chnid]->fd = open(all_chn[chnid]->res.gl_pathv[all_chn[chnid]->next], O_RDONLY);
		if(all_chn[chnid]->fd < 0) {
			perror("open() next failed:");
			return -1;
		}
	}


	return cnt;
}

